﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using System.Text;

using octalforty.Chinchillin.Shared.Deployment;
using octalforty.Chinchillin.Shared.Deployment.Metadata;

namespace octalforty.Chinchillin.Actions.Cert
{
    [Action(Name)]
    public class ImportCertificatesAction : ActionBase
    {
        #region Private Constants
        private const string Name = "import-certificates";

        private static readonly ActionInfo actionInfo = new ActionInfo {
            Name = Name,
            Description = Resources.Cert.ImportCertificatesAction_Description,
            Arguments = new[] { new ActionArgumentInfo("cert-location", ActionArgumentType.Uri) { Description = Resources.Cert.ImportCertificatesAction_CertLocationDescription }, }
            };
        #endregion

        public ImportCertificatesAction() :
            base(actionInfo)
        {
        }

        public override void Execute(IActionExecutionContext executionContext)
        {
            try
            {
                var certLocation = ((Uri)executionContext.Arguments["cert-location"]).AbsolutePath;
                var certificateFiles = Directory.GetFiles(certLocation, "*.cer");

                TracingManager.TraceEvent(executionContext, TraceEventType.Start, 0, string.Format("Started importing certificates from '{0}'. {1} CER files found", 
                    certLocation, certificateFiles.Length));
                
                foreach(var certificateFile in certificateFiles)
                    ImportCertificate(executionContext, certificateFile);

                TracingManager.TraceEvent(executionContext, TraceEventType.Stop, 0, string.Format("Stopped importing certificates from '{0}'", certLocation));
            } // try
            catch(Exception e)
            {
                TracingManager.TraceEvent(executionContext, TraceEventType.Error, 0, e.ToString());
            } // catch
        }

        private void ImportCertificate(IActionExecutionContext ec, string path)
        {
            Verbose(ec, "Opening '{0}'", path);

            var x509Certificate = new X509Certificate2(path, "", 
                X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);

            Verbose(ec, "Importing '{0}'", x509Certificate.SubjectName.Name);

            var storePermission = new StorePermission(PermissionState.Unrestricted)
                { Flags = StorePermissionFlags.OpenStore | StorePermissionFlags.AddToStore | StorePermissionFlags.RemoveFromStore };
            storePermission.Assert();

            var x509Store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            
            x509Store.Open(OpenFlags.ReadWrite);

            Verbose(ec, "Deleting existing certificate");

            var existingCertificates = x509Store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, x509Certificate.SubjectName.Name, false);
            if(existingCertificates.Count > 0)
                x509Store.RemoveRange(existingCertificates);

            x509Store.Add(x509Certificate);
            x509Store.Close();

            Verbose(ec, "Imported '{0}'", x509Certificate.SubjectName.Name);
        }

        private void Verbose(IActionExecutionContext executionContext, string format, params object[] args)
        {
            TracingManager.TraceEvent(executionContext, TraceEventType.Verbose, 0, string.Format(format, args));
        }
    }
}
